﻿using System;
using System.IO.Pipes;
using System.Text;

namespace SimplePipeHelper.Client
{
    public class SimplePipeClient : IDisposable
    {
        public event Action<SimplePipeClient, bool> PipeClientConnect = (pipeClient, ret) => { }; //管道客户端连接
        public event Action<SimplePipeClient> PipeClientClosed = (pipeClient) => { }; //主动关闭管道客户端
        public event Action<SimplePipeClient> PipeServerClosed = (pipeClient) => { }; //和管道服务端断开连接
        public event Action<SimplePipeClient, byte[]> DataReceived = (pipeClient, buffer) => { }; //管道客户端接收到数据

        public bool IsLocalHost { get; set; } = true;
        public string Host { get; set; } = "."; //远程主机名称
        public string UserName { get; set; } //远程主机账号
        public string Password { get; set; } //远程主机密码

        public string PipeName { get; set; } //管道名称
        public uint TimeOut { get; set; } = 10; //超时毫秒数
        public string LastErrorMessage { get; set; } //错误信息

        private NamedPipeClientStream _pipeClient;
        private readonly PipeClientReadHelper _readHelper;
        private readonly PipeClientWriteHelper _writeHelper;

        public SimplePipeClient(int maxReadBufferSize = 4096)
        {
            _readHelper = new PipeClientReadHelper(
                OnPipeServerClosed,
                OnDataReceived,
                OnErrorMessage,
                maxReadBufferSize);
            _writeHelper = new PipeClientWriteHelper(
                OnPipeServerClosed,
                OnErrorMessage);
        }

        #region pipe callback

        private void OnPipeServerClosed()
        {
            PipeServerClosed?.Invoke(this);
        }

        private void OnDataReceived(byte[] buffer)
        {
            DataReceived?.Invoke(this, buffer);
        }

        private void OnErrorMessage(string errMsg)
        {
            LastErrorMessage = errMsg;
        }

        #endregion

        public void Connect()
        {
            if (string.IsNullOrEmpty(PipeName))
            {
                throw new Exception("PipeName is null or empty.");
            }

            try
            {
                _pipeClient = new NamedPipeClientStream(
                    Host,
                    PipeName,
                    PipeDirection.InOut,
                    PipeOptions.Asynchronous);
                _pipeClient.Connect((int) TimeOut);
                _pipeClient.ReadMode = PipeTransmissionMode.Byte;

                try
                {
                    if (_pipeClient.CanTimeout)
                    {
                        _pipeClient.WriteTimeout = (int) TimeOut;
                        _pipeClient.ReadTimeout = (int) TimeOut;
                    }
                }
                catch (InvalidOperationException)
                {
                    //Do nothing
                }

                //开始读写数据
                _readHelper.Start(_pipeClient);
                _writeHelper.Start(_pipeClient);

                PipeClientConnect?.Invoke(this, true);
            }
            catch (Exception e)
            {
                LastErrorMessage = e.Message;
                PipeClientConnect?.Invoke(this, false);
            }
        }

        public void Close()
        {
            //停止读写数据
            _readHelper.Stop();
            _writeHelper.Stop();

            //关闭管道
            if (_pipeClient != null)
            {
                if (_pipeClient.IsConnected)
                {
                    _pipeClient.Close();
                }

                _pipeClient.Dispose();
                _pipeClient = null;

                PipeClientClosed?.Invoke(this);
            }
        }

        public void SendMessage(string message)
        {
            SendMessage(Encoding.UTF8.GetBytes(message));
        }

        public void SendMessage(byte[] buffer)
        {
            _writeHelper.AddMessage(buffer);
        }

        #region IDisposable Support

        //检测冗余调用
        private bool _disposedValue;

        private void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                {
                    //释放托管状态(托管对象)。
                }

                //释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                //将大型字段设置为 null。
                Close();

                _disposedValue = true;
            }
        }

        ~SimplePipeClient()
        {
            Dispose(false);
        }

        // 添加此代码以正确实现可处置模式。
        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
